/**
 * 
 */
package ua.lviv.franko.solvers.twoDimention;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import org.jzy3d.maths.Range;
import org.jzy3d.plot3d.builder.Mapper;

import ua.lviv.franko.integration.ConstantFunction2D;
import ua.lviv.franko.integration.FunctionWithoutDerivatives2D;
import ua.lviv.franko.matrix.RowType;
import ua.lviv.franko.matrix.hash.lu.HashMatrixLUStyle;
import ua.lviv.franko.matrix.hash.lu.LUFactorization;
import ua.lviv.franko.solvers.ElementMatrix;
import ua.lviv.franko.solvers.IElement2D;
import ua.lviv.franko.triangulation.SaveStructures.Node;
import ua.lviv.franko.triangulation.SaveStructures.Rectangle;
import ua.lviv.franko.visualisation.Visualise3D;

/**
 * @author marko
 * 
 */
public class Solver {

	protected ArrayList<IElement2D>	elementsList;
	protected HashMatrixLUStyle			mainMatrix;
	protected double[]				b;

	public static int				number	= 0;

	public Solver(ArrayList<IElement2D> elementsList) {
		this.elementsList = elementsList;
	}

	public void generate() {
		// int N = elementsList.get(elementsList.size()-1).lastNumber()+1;
		int N = number;
		mainMatrix = new HashMatrixLUStyle(RowType.ARRAY_TYPE, N);
		b = new double[N];

		for (Iterator<IElement2D> iterator = elementsList.iterator(); iterator.hasNext();) {
			IElement2D element = iterator.next();
			element.generateAll();
			ElementMatrix data = element.getAll();
			List<Integer> pos = data.getNodeList();
			for (int i = 0; i < pos.size(); i++) {
				for (int j = 0; j < pos.size(); j++) {
					mainMatrix.setValue(pos.get(j), pos.get(i), mainMatrix.getValue(pos.get(j), pos.get(i)) + data.matrix[j][i]);
				}
				b[pos.get(i)] += data.vector[i];
			}
		}
		// System.out.println("-----------------");
		// for (int i = 0; i < b.length; i++) {
		// for (int j = 0; j < b.length; j++) {
		// System.out.print(mainMatrix.getValue(i, j)+"\t");
		// }
		// System.out.print("\n");
		// // System.out.println(b[i]);
		// }
		System.out.println("-----------------");

	}

	public double[] solve() {
		double eps = 0.000000000001;
		for (Iterator<IElement2D> iterator = elementsList.iterator(); iterator.hasNext();) {
			IElement2D elem = iterator.next();
			// if
			// ((Math.abs(elem.getA().x-0.0)<eps)||(Math.abs(elem.getA().y-0.0)<eps)||(Math.abs(elem.getA().x-1.0)<eps)||(Math.abs(elem.getA().y-1.0)<eps)){
			// setBC(elem.getPos().get(0), 0.0);
			// }
			// if
			// ((Math.abs(elem.getB().x-0.0)<eps)||(Math.abs(elem.getB().y-0.0)<eps)||(Math.abs(elem.getB().x-1.0)<eps)||(Math.abs(elem.getB().y-1.0)<eps)){
			// setBC(elem.getPos().get(1), 0.0);
			// }
			// if
			// ((Math.abs(elem.getC().x-0.0)<eps)||(Math.abs(elem.getC().y-0.0)<eps)||(Math.abs(elem.getC().x-1.0)<eps)||(Math.abs(elem.getC().y-1.0)<eps)){
			// setBC(elem.getPos().get(2), 0.0);
			// }
			// if
			// ((Math.abs(elem.getD().x-0.0)<eps)||(Math.abs(elem.getD().y-0.0)<eps)||(Math.abs(elem.getD().x-1.0)<eps)||(Math.abs(elem.getD().y-1.0)<eps)){
			// setBC(elem.getPos().get(3), 0.0);
			// }
			double val = 0.0;
			double pos = 0.0;
			if ((Math.abs(elem.getA().x - pos) < eps)) {
				setBC(elem.getPos().get(0), val);
			}
			if ((Math.abs(elem.getB().x - pos) < eps)) {
				setBC(elem.getPos().get(1), val);
			}
			if ((Math.abs(elem.getC().x - pos) < eps)) {
				setBC(elem.getPos().get(2), val);
			}
			if ((Math.abs(elem.getD().x - pos) < eps)) {
				setBC(elem.getPos().get(3), val);
			}
			val = 1;
			pos = 3;
			if ((Math.abs(elem.getA().x - pos) < eps)) {
				setBC(elem.getPos().get(0), val);
			}
			if ((Math.abs(elem.getB().x - pos) < eps)) {
				setBC(elem.getPos().get(1), val);
			}
			if ((Math.abs(elem.getC().x - pos) < eps)) {
				setBC(elem.getPos().get(2), val);
			}
			if ((Math.abs(elem.getD().x - pos) < eps)) {
				setBC(elem.getPos().get(3), val);
			}

		}

		LUFactorization lu = new LUFactorization(this.mainMatrix);
		return lu.calculate(b);
	}

	private void setBC(int pos, double val) {
		for (int i = 0; i < mainMatrix.getN(); i++) {
			mainMatrix.setValue(pos, i, 0.0);
		}
		mainMatrix.setValue(pos, pos, 1.0);
		b[pos] = val;
	}

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		// ArrayList<Node> nodeList = new ArrayList<Node>();
		double ax = 0, bx = 3, ay = 0, by = 1;

		int n = 25, m = 5;

		double hx = (bx - ax) / (double) n, hy = (by - ay) / (double) m;

		// вузли+функції:
		for (int i = 0; i <= m; i++) {
			for (int j = 0; j <= n; j++) {
				Node p = new Node(ax + j * hx, ay + i * hy, false, i * n + j);
				System.out.println(p.x + "\t" + p.y);
				// if ((i==0)||(j==0)) {
				// p.IsOnBoundery=true;
				// }
				// nodeList.add(p);
			}
		}
		ArrayList<IElement2D> elems = new ArrayList<IElement2D>();
		EquationParameters eParams = new EquationParameters();
		eParams.setF(new FunctionWithoutDerivatives2D() {

			@Override
			public double calculate(double x, double y) {
				return 25 * Math.sin(x / 4.0);
			}
		});
		eParams.setKxx(new ConstantFunction2D(1));
		eParams.setKyy(new ConstantFunction2D(1));
		eParams.setKxy(new ConstantFunction2D(0));
		eParams.setKyx(new ConstantFunction2D(0));
		eParams.setPx(new ConstantFunction2D(10));
		eParams.setPy(new ConstantFunction2D(0));
		eParams.setQ(new ConstantFunction2D(10));

		number = (n + 1) * (m + 1);

		for (int i = 0; i < m; i++) {
			for (int j = 0; j < n; j++) {
				// if(i==m-1 && j==n-1) continue;
				Rectangle r = new Rectangle();
				r.A = new Node(ax + j * hx, ay, false, 0);
				r.B = new Node(ax + (j + 1) * hx, ay, false, 0);
				r.C = new Node(ax + (j + 1) * hx, ay + hy, false, 0);
				r.D = new Node(ax + j * hx, ay + hy, false, 0);
				// if ((i==0)||(i==n)) {
				// r.A.IsOnBoundery=true;
				// }

				LinearRectangularElement elem = new LinearRectangularElement(i * (n + 1) + j, i * (n + 1) + j + 1, (i + 1) * (n + 1) + j + 1, (i + 1) * (n + 1) + j, r.A, r.B, r.C,
						r.D, eParams);
				// EnrichedLinearRectangularElement elem = new
				// EnrichedLinearRectangularElement(i*(n+1)+j, i*(n+1)+j+1,
				// (i+1)*(n+1)+j+1, (i+1)*(n+1)+j, r.A, r.B, r.C, r.D,eParams,
				// 3, 3);
				elems.add(elem);
			}
			ay += hy;
		}
		ay = 0.0;
		// int i = m-1;
		// int j = n-1;
		// // for (int j = 0; j < n; j++) {
		// Rectangle r = new Rectangle();
		// r.A = new Node(ax + j*hx, ay , false, 0);
		// r.B = new Node(ax + (j+1)*hx, ay , false, 0);
		// r.C = new Node(ax + (j+1)*hx, ay + hy , false, 0);
		// r.D = new Node(ax + j*hx, ay + hy , false, 0);
		// // if ((i==0)||(i==n)) {
		// // r.A.IsOnBoundery=true;
		// // }
		//
		// // LinearRectangularElement elem = new
		// LinearRectangularElement(i*(n+1)+j, i*(n+1)+j+1, (i+1)*(n+1)+j+1,
		// (i+1)*(n+1)+j, r.A, r.B, r.C, r.D,eParams);
		// EnrichedLinearRectangularElement elem = new
		// EnrichedLinearRectangularElement(i*(n+1)+j, i*(n+1)+j+1,
		// (i+1)*(n+1)+j+1, (i+1)*(n+1)+j, r.A, r.B, r.C, r.D,eParams, 5, 5);
		// elems.add(elem);
		// }

		Solver s = new Solver(elems);
		s.generate();
		double[] res = s.solve();
		for (int k = 0; k < res.length; k++) {
			System.out.println(res[k]);
		}
		final ArrayList<IElement2D> el = elems;
		final double[] vals = res;

		Visualise3D.show(new Mapper() {
			@Override
			public double f(double x, double y) {
				double val = 0.0;
				int i = 0;
				while (val == 0.0 && i < el.size()) {
					val += el.get(i).calculate(x, y, vals);
					i++;
				}

				// return ff.calculateDerivatyY(x, y);//(x, y);
				return val;
			}
		}, new Range(ax, bx), new Range(ay, by));

	}

}
